#!/usr/bin/python

import sys
import rdb
import struct

#RDB_FILE_NAME = "usc77001.rdm.original.2008-04-24"
#RDB_FILE_NAME = "usc77001.rdm.original.2008-05-22"  # newer
if len(sys.argv) != 2:
    print >> sys.stderr, "Usage:\n\t%s <rdb_file_name>\nOutput:\n\tpoints.shp, polylines.shp, polyline.names.utf8.txt" % sys.argv[0]
    sys.exit(-1)
tables = rdb.rdb_load(sys.argv[1])
rdb.rdb_partial_parse(tables)



##point_data_table=[  # 83350 records
##  { 'longitude':0, 'latitude':11437 },
##  { 'longitude':18909, 'latitude':28929 },
##]
##
#point_id_table=[  # 83350 records
#]
##
##square_global_table={ # 1 records
##  'edges.east':35866352,
##  'edges.north':33334655,
##  'edges.west':34219848,
##  'edges.south':29489368,
##  'step_longitude':32284,
##  'step_latitude':32587,
##  'count_longitude':51,
##  'count_latitude':118,
##  'count_squares':1413,
##}
##square_data_table=[ # 1413 records
##  { #'location index':0,
##    'edges.east':34252132,
##    'edges.north':31346827,
##    'edges.west':34219848,
##    'edges.south':31314240,
##    'position':56,
##    'first_point':0, # (00000000)
##    'first_shape':0, },
##  { #'location index':1,
##    'edges.east':34284416,
##    'edges.north':31216479,
##    'edges.west':34252132,
##    'edges.south':31183892,
##    'position':170,
##    'first_point':1, # (00000001)
##    'first_shape':85, },
##]
##line_data_table=[ # 101180 records
##  { 'line_pos_index':0, 'from':0, 'from_as_hex':0x00000000, 'to':26148864, 'to_as_hex':0x018F0000, 'first_shape':0, 'street':0 },
##  { 'line_pos_index':1, 'from':65537, 'from_as_hex':0x00010001, 'to':327681, 'to_as_hex':0x00050001, 'first_shape':0, 'street':0 },
##]
##street_name_table=[ # 12449 records
##    { 'index_in_table':0, 'fedirp':0, 'fename':1567, 'fetype':0, 'fedirs':0 },
##    { 'index_in_table':1, 'fedirp':0, 'fename':0, 'fetype':0, 'fedirs':0 },
##]
##street_type_table=[ # 12449 records
##    5, # <one_street_type index_in_table='0' as_byte='5' as_hex='05' />
##    9, # <one_street_type index_in_table='1' as_byte='9' as_hex='09' />
##    10, # <one_street_type index_in_table='2' as_byte='10' as_hex='0A' />
##]


##point_data_table=[  # 83350 records
##  { 'longitude':0, 'latitude':11437 },
##  { 'longitude':18909, 'latitude':28929 },
##]



########
"""
Shapefile shape format (.shp)

The main file (.shp) contains the primary geographic reference data in the shapefile.
The file consists of a single fixed length header followed by one or more variable length records.
Each of the variable length records includes a record header component and a record contents component.
A detailed description of the file format is given in the ESRI Shapefile Technical Description.[1]
This format should not be confused with the AutoCAD shape font source format, which shares the .shp extension.

The main file header is fixed at 100 bytes in length and contains 17 fields;
nine 4-byte (32-bit unsigned integer or uint32) integer fields followed by eight 8-byte (double) floating point fields:
Bytes 	Type 	Endianness 	Usage
0-3 	uint32 	big 	File code (always hex value 0x0000270a)
4-23 	uint32 	big 	Unused; five uint32
24-27 	uint32 	big 	File length (in 16-bit words)
28-31 	uint32 	little 	Version
32-35 	uint32 	little 	Shape type (see reference below)
36-67 	double 	little 	Minimum bounding rectangle (MBR) of all shapes contained within the shapefile; four doubles in the following order: min X, min Y, max X, max Y
68-83 	double 	little 	Range of Z; two doubles in the following order: min Z, max Z
84-99 	double 	little 	Range of M; two doubles in the following order: min M, max M

The file then contains any number of variable-length records. Each record is prefixed with a record-header of 8 bytes:
Bytes 	Type 	Endianness 	Usage
0-3 	uint32 	big 	Record number
4-7 	uint32 	big 	Record length (in 16-bit words)

Following the record header is the actual record:
Bytes 	Type 	Endianness 	Usage
0-3 	uint32 	little 	Shape type (see reference below)
4- 	- 	- 	Shape content

The variable length record contents depend on the shape type. The following are the possible shape types:
Value 	Shape type 	Fields
0 	Null shape 	None
1 	Point 	X, Y
3 	Polyline 	MBR, Number of parts, Number of points, Parts, Points
5 	Polygon 	MBR, Number of parts, Number of points, Parts, Points
8 	MultiPoint 	MBR, Number of points, Points
11 	PointZ 	X, Y, Z, M
13 	PolylineZ 	Mandatory: MBR, Number of parts, Number of points, Parts, Points, Z range, Z array

Optional: M range, M array
15 	PolygonZ 	Mandatory: MBR, Number of parts, Number of points, Parts, Points, Z range, Z array

Optional: M range, M array
18 	MultiPointZ 	Mandatory: MBR, Number of points, Points, Z range, Z array

Optional: M range, M array
21 	PointM 	X, Y, M
23 	PolylineM 	Mandatory: MBR, Number of parts, Number of points, Parts, Points

Optional: M range, M array
25 	PolygonM 	Mandatory: MBR, Number of parts, Number of points, Parts, Points

Optional: M range, M array
28 	MultiPointM 	Mandatory: MBR, Number of points, Points

Optional Fields: M range, M array
31 	MultiPatch 	Mandatory: MBR, Number of parts, Number of points, Parts, Part types, Points, Z range, Z array

Optional: M range, M array
"""
#######

point_data_table = tables["point"]["data"]
line_data_table  = tables["line"]["data"]

all_x_vals = map(lambda point: point['_abs_lon'], point_data_table) # x
all_y_vals = map(lambda point: point['_abs_lat'], point_data_table) # y
min_x_val = min(all_x_vals)
max_x_val = max(all_x_vals)
min_y_val = min(all_y_vals)
max_y_val = max(all_y_vals)
print "min x,y:", min_x_val, min_y_val
print "max x,y:", max_x_val, max_y_val

def build_shp_file_hdr(data_size_in_bytes):
    result = ""
    result = result + struct.pack('>L',0x0000270a)  #uint32 	big 	File code (always hex value 0x0000270a)
    result = result + struct.pack('>5L',0,0,0,0,0)  #uint32 	big 	Unused; five uint32
    result = result + struct.pack('>L',50+(data_size_in_bytes/2))#uint32 	big 	File length (in 16-bit words)
    result = result + struct.pack('<L',1000)        #uint32 	little 	Version
    result = result + struct.pack('<L',1)           #uint32 	little 	Shape type (see reference below)  (1=point)
    result = result + struct.pack('<4d',min_x_val,min_y_val,max_x_val,max_y_val)#double 	little 	Minimum bounding rectangle (MBR) of all shapes contained within the shapefile;
    #                                               #four doubles in the following order: min X, min Y, max X, max Y
    result = result + struct.pack('<2d',0,0)         #double 	little 	Range of Z; two doubles in the following order: min Z, max Z
    result = result + struct.pack('<2d',0,0)         #double 	little 	Range of M; two doubles in the following order: min M, max M
    return result

global_last_record_number = 0

def build_shp_point_record(x,y):
    result = ""
    # record header:
    global global_last_record_number
    global_last_record_number = global_last_record_number + 1
    result = result + struct.pack('>L',global_last_record_number)  #uint32 	big 	Record number
    result = result + struct.pack('>L',10)   #uint32 	big 	Record length (in 16-bit words)
    result = result + struct.pack('<L',1)    #uint32 	little 	shape type (1=point)
    result = result + struct.pack('<d',x)    #double 	little  x
    result = result + struct.pack('<d',y)    #double 	little  y
    return result


##
## output data: points
##
file_data = ""
for i in range(0, len(point_data_table)):
    point = point_data_table[i]
    y = point['_abs_lat']
    x = point['_abs_lon']
    # record number = i+1
    file_data = file_data + build_shp_point_record(x,y)


# add shape file header header before the 
file_data = build_shp_file_hdr(len(file_data)) + file_data

f = open('points.shp', 'wb')
f.write(file_data)
f.close()
print "-- done writing shapefile"



def build_shp_file_hdr_2(data_size_in_bytes):
    result = ""
    result = result + struct.pack('>L',0x0000270a)  #uint32 	big 	File code (always hex value 0x0000270a)
    result = result + struct.pack('>5L',0,0,0,0,0)  #uint32 	big 	Unused; five uint32
    result = result + struct.pack('>L',50+(data_size_in_bytes/2))#uint32 	big 	File length (in 16-bit words)
    result = result + struct.pack('<L',1000)        #uint32 	little 	Version
    result = result + struct.pack('<L',3)           #uint32 	little 	Shape type (see reference below)  (1=point, 3=Polyline)
    result = result + struct.pack('<4d',min_x_val,min_y_val,max_x_val,max_y_val)#double 	little 	Minimum bounding rectangle (MBR) of all shapes contained within the shapefile;
    #                                               #four doubles in the following order: min X, min Y, max X, max Y
    result = result + struct.pack('<2d',0,0)         #double 	little 	Range of Z; two doubles in the following order: min Z, max Z
    result = result + struct.pack('<2d',0,0)         #double 	little 	Range of M; two doubles in the following order: min M, max M
    return result
    

def build_shp_polyline_record(x1,y1, x2, y2):
    result = ""
    # record header:
    global global_last_record_number
    global_last_record_number = global_last_record_number + 1
    result = result + struct.pack('>L',global_last_record_number)  #uint32 	big 	Record number
    result = result + struct.pack('>L',(8*8+4*4)/2)   #uint32 	big 	Record length (in 16-bit words)
    result = result + struct.pack('<L',3)    #uint32 	little 	shape type (1=point,3=polyline)
    result = result + struct.pack('<4d',min(x1,x2),min(y1,y2),max(x1,x2),max(y1,y2))# double[4] box: xmin,ymin, xmax, ymax  (little)
    result = result + struct.pack('<L',1)# uint32 numParts (little)
    result = result + struct.pack('<L',2)# uint32 totalNumPoints (little)
    result = result + struct.pack('<L',0)# uint32 Parts[NumParts] (little) // index to first point in part
    result = result + struct.pack('<4d',x1,y1,x2,y2)# Point Points[NumPoints] (little) // index to first point in part
    return result


##
## output data: lines
##
file_data = ""
global_last_record_number = 0
#for i in range(0, 101180-1):
#for i in range(0, 1180-1):
for i in range(0, len(line_data_table)):
    line = line_data_table[i]
    point = point_data_table[ line['_from'] ]
    y1 = point['_abs_lat']
    x1 = point['_abs_lon']
    point = point_data_table[ line['_to'] ]
    y2 = point['_abs_lat']
    x2 = point['_abs_lon']
    # record number = i+1
    file_data = file_data + build_shp_polyline_record(x1,y1, x2, y2)
    #print "%g,%g  %g,%g" % (x1,y1,x2,y2)


# add shape file header header before the 
file_data = build_shp_file_hdr_2(len(file_data)) + file_data

f = open('polylines.shp', 'wb')
f.write(file_data)
f.close()
print "-- done writing shapefile"


##
## output data: street names
##

f = open('polylines.names.utf8.txt', 'wt')
for i in range(0, len(line_data_table)):
    print >> f, i, line_data_table[i]['_name']
f.close()



